package com.itheima.stock.service.impl;

import com.google.common.collect.Lists;
import com.itheima.stock.config.TaskExecutePoolConfig;
import com.itheima.stock.face.StockCacheFace;
import com.itheima.stock.mapper.StockBusinessMapper;
import com.itheima.stock.mapper.StockMarketIndexInfoMapper;
import com.itheima.stock.mapper.StockRtInfoMapper;
import com.itheima.stock.pojo.entity.StockMarketIndexInfo;
import com.itheima.stock.pojo.entity.StockRtInfo;
import com.itheima.stock.pojo.vo.StockInfoConfig;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.utils.IdWorker;
import com.itheima.stock.utils.ParseType;
import com.itheima.stock.utils.ParserStockInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;

@Slf4j
@Service
public class StockTimerTaskService implements com.itheima.stock.service.StockTimerTaskService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    @Autowired
    private ParserStockInfoUtil parserStockInfoUtil;

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor; //线程池

    @Autowired
    private StockCacheFace stockCacheFace;

    /**
     * 必须保证该对象无状态  =>不能往里面塞东西set什么什么
     */
    private HttpEntity<Object> httpEntity;



    @Override
    public void getInnerMarketInfo() {
        //1.阶段1：采集原始数据
        //组装url
       // String url="http://hq.sinajs.cn/list=sh000001,sz399001";
        //String.join()方法将第一个参数作为分隔符。在第二个参数中，我们可以传递多个字符串或包含要连接的字符串的可迭代实例。join()方法将返回一个由分隔符分隔的字符串组成的新字符串。
        String url = stockInfoConfig.getMarketUrl()+String.join(",",stockInfoConfig.getInner());
        //1.21.2维护请求头，添加防盗链和用户标识
//        HttpHeaders headers = new HttpHeaders();//org.springframework.http.HttpHeaders;
//        //防盗链
//        headers.add("Referer","https://finance.sina.cn/?from=redirect");
//        //客户端表示 - 我是浏览器
//        headers.add("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1");
//
//        //2.维护http请求实体对象
//        HttpEntity<Object> httpEntity = new HttpEntity<>(headers);

        //发起请求 有请求头使用exchange 没有请求头getForObject
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            //请求失败
            log. error("当时时间{},数据采集失败http状态码{}", DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),
                         responseEntity.getStatusCodeValue());
            //发送邮件给运营人员
            return;
        }
        //获取js格式数据
        String jsDate = responseEntity.getBody();
        log. info("当时时间{},采集的原始数据{}", DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"), jsDate);
        //2.阶段2：java正则解析原始数据
        //2.1定义正则
         String reg = "var hq_str_(.+)=\"(.+)\";";
         //2.2达式编译
         Pattern pattern = Pattern.compile(reg);
         //2.3匹配字符串
        Matcher matcher = pattern.matcher(jsDate);
        List<StockMarketIndexInfo> entities = new ArrayList<>();
        while (matcher.find()){//如果有数据返回真
            //1.获取大盘编码
            String marketCode = matcher.group(1);
            //2.获取其他信息
            String other = matcher.group(2);
           // 3.将other字符串以逗号切割，获取大片的详情信息
            String[] splitArr = other.split(",");


            //大盘名称
            String marketName=splitArr[0];
            //获取当前大盘的开盘点数
            BigDecimal openPoint=new BigDecimal(splitArr[1]);
            //前收盘点
            BigDecimal preClosePoint=new BigDecimal(splitArr[2]);
            //获取大盘的当前点数
            BigDecimal curPoint=new BigDecimal(splitArr[3]);
            //获取大盘最高点
            BigDecimal maxPoint=new BigDecimal(splitArr[4]);
            //获取大盘的最低点
            BigDecimal minPoint=new BigDecimal(splitArr[5]);
            //获取成交量
            Long tradeAmt=Long.valueOf(splitArr[8]);
            //获取成交金额
            BigDecimal tradeVol=new BigDecimal(splitArr[9]);
            //时间
            Date curTime = DateTimeUtil.getDateTimeWithoutSecond(splitArr[30] + " " + splitArr[31]).toDate();


            //3.阶段3：解析数据封装entity

            StockMarketIndexInfo entiey = StockMarketIndexInfo.builder()
                    .id(idWorker.nextId()) //雪花算法素材的id
                    .marketName(marketName)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .curPoint(curPoint)
                    .maxPoint(maxPoint)
                    .minPoint(minPoint)
                    .tradeAmount(tradeAmt)
                    .tradeVolume(tradeVol)
                    .marketCode(marketCode)
                    .curTime(curTime)
                    .build();
            entities.add(entiey);
        }
        log.info("解析数据完毕");
//4.阶段4：调用mybatis批量入库
     int  count=  stockMarketIndexInfoMapper.insrtrBatch(entities);
     if(count>0){
         //大盘数据采集完毕后，通知backend工程刷新缓存
         //发送日期对象，接收方通过接收的日期与当前日期比对，判断出数据延迟的时长，用于运维通知处理
         rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());

         log.info("当前时间：{}，插入大盘数据成功{}",DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),entities);
     }else {
         log.error("当前时间：{}，插入大盘数据失败{}",DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),entities);
     }
    }

    /**
     * 采集个股数据
     */
    @Override
    public void getStockRtIndex() {
        //1.获取所有个股的集合3000+
//        List<String> allStockCode = stockBusinessMapper.getAllStockCode();
//        //编码以6开头加sh 不是加sz
//        //一次性将所有集合拼接到urL地址中，导致地址过长，参数过多
//        allStockCode =  allStockCode.stream().map(code->code.startsWith("6")?"sh"+code:"sz"+code).collect(Collectors.toList());
        //System.out.println(allStockCode);
        List<String> allStockCode= stockCacheFace.getAllStockCodeWithPredix();// springCache,从缓存层获取
        //将所有个股编码组成的大的集合拆分成若干小的集合40--->15 15 10（分片处理）
        List<List<String>> partition = Lists.partition(allStockCode, 15);//com.google.guava 谷歌工具包
        //2.将大的集合切分成若干小集合，分批次拉取数据
       partition.forEach(codes->{
           // 原始方案
//           String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
//            //1.21.2维护请求头，添加防盗链和用户标识
////            HttpHeaders headers = new HttpHeaders();//org.springframework.http.HttpHeaders;
////            //防盗链
////            headers.add("Referer","https://finance.sina.cn/?from=redirect");
////            //客户端表示 - 我是浏览器
////            headers.add("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1");
////
////            //2.维护http请求实体对象
////            HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
//
//            //发起请求 有请求头使用exchange 没有请求头getForObject
//            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//            if (responseEntity.getStatusCodeValue() != 200) {
//                //请求失败
//                log. error("当时时间{},数据采集失败http状态码{}", DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),
//                        responseEntity.getStatusCodeValue());
//                //发送邮件给运营人员
//                return;
//            }
//            //取原始js格式数据
//            String JsDate = responseEntity.getBody();
//            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(JsDate, ParseType.ASHARE);
//            log.info("采集个股数据：{}",list);
//            //todo 批量插入
//            //批量保存采集的个股
//            int count = stockRtInfoMapper.insertBatch(list);
//            if(count>0){
//                log.info("当前时间：{}，插入个股数据成功{}",DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),list);
//            }else {
//                log.error("当前时间：{}，插入个股数据失败{}",DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),list);
//            }



           // 方案1：原始方案采集数据时将集合分片，然后分批次串行采集数据，存在较高的延迟；引入多线程
           // 多线程
           //代码的问题：1.每次来任务，就创建换一个线程，复用性差2.如果多线程使用不当，造成CPU竞争激烈，导致频繁的上下文切换，导致程序性能降低
//           new Thread( ()->{
//                          String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
//            //1.21.2维护请求头，添加防盗链和用户标识
////            HttpHeaders headers = new HttpHeaders();//org.springframework.http.HttpHeaders;
////            //防盗链
////            headers.add("Referer","https://finance.sina.cn/?from=redirect");
////            //客户端表示 - 我是浏览器
////            headers.add("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1");
////
////            //2.维护http请求实体对象
////            HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
//
//            //发起请求 有请求头使用exchange 没有请求头getForObject
//            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//            if (responseEntity.getStatusCodeValue() != 200) {
//                //请求失败
//                log. error("当时时间{},数据采集失败http状态码{}", DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),
//                        responseEntity.getStatusCodeValue());
//                //发送邮件给运营人员
//                return;
//            }
//            //取原始js格式数据
//            String JsDate = responseEntity.getBody();
//            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(JsDate, ParseType.ASHARE);
//            log.info("采集个股数据：{}",list);
//            //todo 批量插入
//            //批量保存采集的个股
//            int count = stockRtInfoMapper.insertBatch(list);
//            if(count>0){
//                log.info("当前时间：{}，插入个股数据成功{}",DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),list);
//            }else {
//                log.error("当前时间：{}，插入个股数据失败{}",DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),list);
//            }
//
//           }).start();



           //方案2：线程池

           threadPoolTaskExecutor.execute(()->{
               String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
            //1.21.2维护请求头，添加防盗链和用户标识
//            HttpHeaders headers = new HttpHeaders();//org.springframework.http.HttpHeaders;
//            //防盗链
//            headers.add("Referer","https://finance.sina.cn/?from=redirect");
//            //客户端表示 - 我是浏览器
//            headers.add("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1");
//
//            //2.维护http请求实体对象
//            HttpEntity<Object> httpEntity = new HttpEntity<>(headers);

            //发起请求 有请求头使用exchange 没有请求头getForObject
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
            if (responseEntity.getStatusCodeValue() != 200) {
                //请求失败
                log. error("当时时间{},数据采集失败http状态码{}", DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),
                        responseEntity.getStatusCodeValue());
                //发送邮件给运营人员
                return;
            }
            //取原始js格式数据
            String JsDate = responseEntity.getBody();
            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(JsDate, ParseType.ASHARE);
            log.info("采集个股数据：{}",list);
            //todo 批量插入
            //批量保存采集的个股
            int count = stockRtInfoMapper.insertBatch(list);
            if(count>0){
                log.info("当前时间：{}，插入个股数据成功{}",DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),list);
            }else {
                log.error("当前时间：{}，插入个股数据失败{}",DateTime.now().toString("yyyy-MM-dd:HH:mm:ss"),list);
            }

           });

        });


    }
    //注解用于标记在 依赖注入完成后需要执行的方法。这个方法在对象创建并完成依赖注入后自动调用，
    // 但在任何类显示调用之前执行。这为开发者提供了一个在对象完全初始化后执行逻辑的钩子。
    @PostConstruct
    public void initData(){

        HttpHeaders headers = new HttpHeaders();//org.springframework.http.HttpHeaders;
        //防盗链
        headers.add("Referer","https://finance.sina.cn/?from=redirect");
        //客户端表示 - 我是浏览器
        headers.add("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1");

        //2.维护http请求实体对象
       httpEntity = new HttpEntity<>(headers);
    }
}
